#include "network_interface.hh"

#include "arp_message.hh"
#include "ethernet_frame.hh"

#include <iostream>

// Dummy implementation of a network interface
// Translates from {IP datagram, next hop address} to link-layer frame, and from link-layer frame to IP datagram

// For Lab 5, please replace with a real implementation that passes the
// automated checks run by `make check_lab5`.

// You will need to add private members to the class declaration in `network_interface.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}

using namespace std;

//! \param[in] ethernet_address Ethernet (what ARP calls "hardware") address of the interface
//! \param[in] ip_address IP (what ARP calls "protocol") address of the interface
NetworkInterface::NetworkInterface(const EthernetAddress &ethernet_address, const Address &ip_address)
    : _ethernet_address(ethernet_address), _ip_address(ip_address) {
  cerr << "DEBUG: Network interface has Ethernet address " << to_string(_ethernet_address) << " and IP address "
       << ip_address.ip() << "\n";
}

EthernetFrame NetworkInterface::broadcast_frame(uint32_t ip) {
  // arp请求体
  ARPMessage arp_msg;
  arp_msg.opcode = ARPMessage::OPCODE_REQUEST;
  arp_msg.sender_ethernet_address = _ethernet_address;
  arp_msg.sender_ip_address = _ip_address.ipv4_numeric();
  arp_msg.target_ethernet_address = {};
  arp_msg.target_ip_address = ip;

  // 帧
  EthernetFrame frame;
  frame.header() = EthernetHeader{ETHERNET_BROADCAST, _ethernet_address, EthernetHeader::TYPE_ARP};
  ;
  frame.payload() = arp_msg.serialize();
  return frame;
}

//! \param[in] dgram the IPv4 datagram to be sent
//! \param[in] next_hop the IP address of the interface to send it to (typically a router or default gateway, but may also be another host if directly connected to the same network as the destination)
//! (Note: the Address type can be converted to a uint32_t (raw 32-bit IP address) with the Address::ipv4_numeric() method.)
void NetworkInterface::send_datagram(const InternetDatagram &dgram, const Address &next_hop) {
  // convert IP address of next hop to raw 32-bit representation (used in ARP header)
  const uint32_t next_hop_ip = next_hop.ipv4_numeric();
  auto it = _arp_table.find(next_hop_ip);
  if (it == _arp_table.end()) {
    // 缓存
    _cache.emplace_back(next_hop, dgram);
    // 还未发送过请求，进行广播
    if (_waiting_arps.find(next_hop_ip) == _waiting_arps.end()) {
      // 广播
      auto frame = broadcast_frame(next_hop_ip);
      _frames_out.push(frame);
      // 等待arp
      _waiting_arps[next_hop_ip] = _time;
    }
  } else {
    EthernetFrame frame;
    frame.header() = {it->second.first, _ethernet_address, EthernetHeader::TYPE_IPv4};
    frame.payload() = dgram.serialize();
    _frames_out.push(frame);
  }
}

//! \param[in] frame the incoming Ethernet frame
optional<InternetDatagram> NetworkInterface::recv_frame(const EthernetFrame &frame) {
  auto &header = frame.header();
  // 只能接收广播地址或本机地址
  if (header.dst != ETHERNET_BROADCAST && header.dst != _ethernet_address) {
    return {};
  }
  if (header.type == EthernetHeader::TYPE_IPv4) {
    InternetDatagram ip_dgram;
    auto result = ip_dgram.parse(frame.payload());
    if (result == ParseResult::NoError) {
      return ip_dgram;
    }
    return {};
  } else {
    ARPMessage arp_msg;
    auto result = arp_msg.parse(frame.payload());
    if (result != ParseResult::NoError) {
      return {};
    }
    // arp回复
    auto sender_eth_addr = arp_msg.sender_ethernet_address;
    auto sender_ip_addr = arp_msg.sender_ip_address;
    if (arp_msg.opcode == ARPMessage::OPCODE_REQUEST && arp_msg.target_ip_address == _ip_address.ipv4_numeric()) {
      EthernetHeader reply_header{sender_eth_addr, _ethernet_address, EthernetHeader::TYPE_ARP};
      ARPMessage reply_arp_msg;
      reply_arp_msg.opcode = ARPMessage::OPCODE_REPLY;
      reply_arp_msg.sender_ethernet_address = _ethernet_address;
      reply_arp_msg.sender_ip_address = _ip_address.ipv4_numeric();
      reply_arp_msg.target_ethernet_address = sender_eth_addr;
      reply_arp_msg.target_ip_address = sender_ip_addr;

      // 回复arp的帧
      EthernetFrame reply_frame;
      reply_frame.header() = reply_header;
      reply_frame.payload() = reply_arp_msg.serialize();

      _frames_out.push(reply_frame);
    }
    // 更新arp表
    _arp_table[sender_ip_addr] = {sender_eth_addr, _time};
    // 删除等待arp回应的报文
    _waiting_arps.erase(sender_ip_addr);
    // 由于更新了arp表，可以把一些待发送数据依次发送
    for (auto it = _cache.begin(); it != _cache.end();) {
      auto &ip_addr = it->first;
      auto &dgram = it->second;
      if (ip_addr.ipv4_numeric() == sender_ip_addr) {
        send_datagram(dgram, ip_addr);
        it = _cache.erase(it);
      } else {
        ++it;
      }
    }
  }
  return {};
}

//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method
void NetworkInterface::tick(const size_t ms_since_last_tick) {
  _time += ms_since_last_tick;
  constexpr size_t valid_internal = 30 * 1000;
  // 更新arp表, 删除掉过期的映射
  for (auto it = _arp_table.begin(); it != _arp_table.end();) {
    auto start_time = it->second.second;
    if (_time - start_time >= valid_internal) {
      it = _arp_table.erase(it);
    } else {
      ++it;
    }
  }
  constexpr size_t repeat_arp_internal = 5 * 1000;
  // 更新等待arp回应的帧
  for (auto it = _waiting_arps.begin(); it != _waiting_arps.end(); ++it) {
    if (_time - it->second >= repeat_arp_internal) {
      auto frame = broadcast_frame(it->first);
      _frames_out.push(frame);
      it->second = _time;
    }
  }
}
